home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Programmer Power Tools
/
Programmer Power Tools.iso
/
keyboard
/
kbedit20.asm
< prev
next >
Wrap
Assembly Source File
|
1987-12-11
|
75KB
|
3,325 lines
TITLE KEYBOARD BUFFER
PAGE 60, 132
;**********************************************************************
;* Keyboard Buffer and Editor VERSION 2.0
;*
;* This program provides two independent functions:
;* 1) it extends the DOS_keyboard_buffer to 128 characters and
;* 2) it provides command-line storage and editing facilities
;*
;* Source: self contained 8088/8086 Macro Assembler Language
;* Script: MASM KBEDIT;
;* LINK KBEDIT;
;* EXE2BIN KBEDIT.EXE KBEDIT.SYS
;*
;* Usage: install as a device driver by including a statement
;* in the CONFIG.SYS file of the form:
;*
;* DEVICE=KBEDIT.SYS [B:nnn] [C:nnn] [K:nnn] [/A] [/N]
;*
;* B:nnn is buffer size for stored strings in bytes
;* (default = 512)
;* C:nnn is maximum number of strings in the string buffer
;* (default = 32)
;* K:nnn is keyboard buffer size in characters
;* (default = 128)
;*
;**********************************************************************
JMPS MACRO LABEL
JMP SHORT LABEL
ENDM
CLR MACRO REG
SUB REG, REG
ENDM
GET_CHAR MACRO
;----- return keyboard input in AL through DOS-call
;
MOV AH, 8 ;; function number
INT 21H
ENDM
; -----DOS-keyboard pointers
;
DOS_DATA_AREA SEGMENT AT 40H
ORG 17H
DOS_KB_FLAG DB ?
ORG 1AH
KB_HEAD DW ?
KB_TAIL DW ?
ORG 71H
DOS_BREAK_FLAG DW ?
ORG 80H
DOS_BUFFER_START DW ?
DOS_BUFFER_END DW ?
DOS_DATA_AREA ENDS
;----- interrupt_vector_adresses_equates
;
KB_INTERRUPT EQU 4*9H
KB_IO_ROUTINE EQU 4*16H
DOS_FUNCTION_INT EQU 4*21H
;----- equates
;
NULL EQU 00H
CR EQU 0DH
LF EQU 0AH
BACKSPACE EQU 08H
ESCAPE EQU 1BH
CTRL_U EQU 15H
CTRL_W EQU 17H
EOF EQU 1AH
ACK EQU 06H
TAB EQU 09H
OFF EQU 0
ON EQU NOT OFF
NOT_INST EQU 0AAH ;
;-----
; the following keys are identified by extended ascii-codes
; the second byte is given in these equates
;
INSERT EQU 052H
DELETE EQU 053H
CURSOR_LEFT EQU 04BH
CURSOR_RIGHT EQU 04DH
PREV_LINE EQU 048H ; = cursor up
NEXT_LINE EQU 050H ; = cursor down
CURSOR_BEGIN EQU 047H ; = home key
CURSOR_END EQU 04FH ; = end key
F1 EQU 03BH
F2 EQU 03CH
F3 EQU 03DH
F4 EQU 03EH
F5 EQU 03FH
F6 EQU 040H
F7 EQU 041H
CTRL_LEFT EQU 073H
CTRL_RIGHT EQU 074H
CTRL_HOME EQU 077H
CTRL_END EQU 075H
CTRL_PGUP EQU 084H
CTRL_PGDN EQU 076H
SHIFT_F5 EQU 058H
SHIFT_F6 EQU 059H
ALT_1 EQU 078H
ALT_2 EQU 079H
ALT_3 EQU 07AH
ALT_4 EQU 07BH
ALT_5 EQU 07CH
ALT_6 EQU 07DH
ALT_7 EQU 07EH
ALT_8 EQU 07FH
ALT_9 EQU 080H
ALT_0 EQU 081H
CODE SEGMENT PUBLIC
ASSUME CS:CODE
START_CODE = $
HEADER DD -1 ; device_driver obligatory
; first four bytes
ATTRIBUTE DW 8000H ; attribute byte:
DW D_STRATEGY
DW D_INTERRUPT
DRVNAME DB 'KBEDT '
;----- installation messages
;
MSG_VERSION DB 'Keyboard Buffer and Command Line Editor,'
DB ' Version 2.0', CR, LF, LF, NULL
DB ' author: P. Kranenburg'
DB ' University of Leiden, The Netherlands'
;----- keyboard related messages
;
MSG_KB_1 DB ' Keyboard buffer size : ', NULL
MSG_KB_2 DB ' characters', CR, LF, NULL
MSG_KB_NO_1 DB ' KeyBoard Buffer size ', NULL
MSG_KB_NO_2 DB ' is too small: not installed', CR, LF, NULL
MSG_KB_IN DB ' KeyBoard Buffer Installed'
DB CR, LF, NULL
MSG_KB_UN DB ' KeyBoard Buffer Uninstalled'
DB CR, LF, NULL
MSG_KB_PR DB ' Keyboard Buffer already installed'
DB CR, LF, NULL
MSG_KB_NOT_PR DB ' Keyboard Buffer was not active'
DB CR, LF, NULL
;----- editor related messages
;
MSG_EDT_1 DB ' Command line buffer size : ', NULL
MSG_EDT_2 DB ' bytes', CR, LF
DB ' Number of commands in buffer : ', NULL
MSG_EDT_3 DB ' ', CR, LF, LF, NULL
MSG_EDT_IN DB ' Command Line Editor Installed'
DB CR, LF, NULL
MSG_EDT_UN DB ' Command Line Editor Uninstalled'
DB CR, LF, NULL
MSG_EDT_PR DB ' Command Line Editor already installed'
DB CR, LF, NULL
MSG_EDT_NOT_PR DB ' Command Line Editor was not active'
DB CR, LF, NULL
;----- pointers to request_header
;
REQ_HEADER_OFS DW ?
REQ_HEADER_SEG DW ?
;----- control data sent to output-routine of driver
;
EXPECTSECOND DB OFF ; first/second byte indicator
FIRSTPART DB LOW ON ; first byte of incoming control codes
KB_INST DB OFF ; keyboard buffer presence flag
EDT_INST DB NOT_INST; keyboard editor presence flag
BIOS_TTY_SW DB LOW ON ; true on driver initialization
TEN DW 10 ; operand for MULtiply operation
;----- local stack for processing buffered input function call
;
LOCAL_STACK DW 256 DUP(?)
;----- containers for user stack-segment and -pointer
;
CALLERS_STACK_SEGMENT DW ?
CALLERS_STACK_POINTER DW ?
RET_FROM_STACK_ROUTINES DW ? ; temp var for SETSTACK and RESETSTACK
;----- routines to switch stacks
;
SETSTACK PROC NEAR
POP RET_FROM_STACK_ROUTINES ; save return address
MOV CALLERS_STACK_SEGMENT, SS ; now save user stack
MOV CALLERS_STACK_POINTER, SP ;
PUSH CS
POP SS ; install new stack
MOV SP, OFFSET LOCAL_STACK + SIZE LOCAL_STACK
JMP RET_FROM_STACK_ROUTINES ; return
SETSTACK ENDP
RESETSTACK PROC NEAR
POP RET_FROM_STACK_ROUTINES
PUSH CALLERS_STACK_SEGMENT
POP SS ; restore stack registers
MOV SP, CALLERS_STACK_POINTER ;
JMP RET_FROM_STACK_ROUTINES
RESETSTACK ENDP
;----- enlarged keyboard buffer
;
KBBUFFER_SIZE DW 256
KBBUFFER DW ?
;----- containers for old interrupt_vector_adresses
;
OLD_KEYB_IO DD ?
OLD_KEYB_INT DD ?
OLD_FUNCTION_INT DD ?
OLD_DOS_BUFFER_START DW ?
OLD_DOS_BUFFER_END DW ?
;#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
;#
;# *** Description of data-structure used in Editor-routines ***
;#
;# Quantities Assignments
;#
;#
;# - actice/inactive switch - - - EDT_INST
;#
;# Buffer data:
;#
;# - current character pointer in buffer - - - register DI
;# - current number of characters in buffer - - - register BL
;# - current number of characters up to but - - - register BH
;# not including the current cursor
;# - maximum number of characters in buffer - - - register DL
;# exclusive final Carriage Return
;# - insert/overwrite mode - - - INSERT_TOGGLE
;#
;# Display data:
;#
;# - screen coordinates of start displayed string- - - STR_START_POS
;# - screen coordinates of end displayed string - - - STR_END_POS
;# - current video page - - - VIDEO_PAGE
;# - current cursor position on the screen - - - CURSOR_POS
;# - cursor shape - - - CURSOR_TYPE
;# - current screen width - - - MAX_COL
;# - number of character rows - - - MAX_ROW (fixed:25)
;#
;# - full/partial editing capability - - - FULL_EDIT
;# - defer-mode when redirected input/output - - - DEFER_DSPL
;# - cursor positioning through ANSI-escape codes- - - ANSI
;#
;#
;#
;# Lay-out of the buffer passed through DS:DX
;#
;#
;# +----------+----------+-------+--/ /--+---------+-------+--/ /--+--------+
;# | BufLen | #chars | D(1) | ... |D(#chars)| 'CR' | ... | N+3 |
;# | = N | in buf | | | | | | |
;# +----------+----------+-------+--/ /--+---------+-------+--/ /--+--------+
;#
;# at offset 0: length of buffer (exclusive first two bytes and final CR)
;# 1: length of actual number of characters in buffer
;# (exclusive final CR)
;# 2 .. [byte(1)]+1 : data
;# [byte(2)]+2 : 'Carriage Return'
;#
;#
;# The buffer can contain a string on input as well. This is checked for at
;# entrance ( a carriage return must be present at the byte
;# with index: [byte(2)]+2 )
;# If present the input string is inserted in the string buffer to make it
;# available for editing.
;#
;#
;#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
INSERT_TOGGLE DB OFF
DEFER_DSPL DB OFF
FULL_EDIT DB OFF
CURSOR_TYPE DW ? ; cursor-status on entry
CURSOR_MASK EQU 00011111B ; mask for set cursor
INSERT_SHIFT DB 4 ; determines cursor-shape
; change in INSERT-mode
CURSOR_POS LABEL WORD
CURSOR_COL DB ?
CURSOR_ROW DB ?
STR_START_POS LABEL WORD
STR_START_COL DB ?
STR_START_ROW DB ?
STR_END_POS LABEL WORD
STR_END_COL DB ?
STR_END_ROW DB ?
MAX_ROW EQU 25
MAX_COL DB 80 ; assumed default for display-width
VIDEO_PAGE DB 0
ANSI DB OFF
ANSI_GET_CURSOR DB ESCAPE, '[6n', NULL
DEVICE_PROLOGUE PROC NEAR
;****************************************************************
;* Find out about devices attached to standard input and ouput by
;* issuing a IOCTL (044H) function call.
;*
;* If either STDIN or STDOUT is attached to a file then writing
;* characters to STDOUT is deferred until editing is complete.
;*
;* If STDOUT is NOT the Console Output then direct cursor
;* manipulation is disabled, i.e. when a multiple line string,
;* the cursor position on the screen does not reflect the current
;* position in the string.
;*
;*
STDIN EQU 0
STDOUT EQU 1
ISDEV EQU 0080H
ISCOT EQU 0002H
PUSH DX
PUSH BX
;----- initialize variables
;
MOV DEFER_DSPL, ON
MOV FULL_EDIT, OFF;
MOV STR_START_POS, 0
MOV STR_END_POS, 0
MOV CURSOR_POS, 0
;----- check standard input
;
MOV BX, STDIN ; BX is handle
MOV AX, 4400H ; AL = 0: get device information
INT 21H ; status returned in DX
TEST DX, ISDEV
JZ DP_LX ; if bit off then file
; else device
;----- check standard output
;
MOV BX, STDOUT ; BX is handle
MOV AX, 4400H ; AL = 0: get device information
INT 21H ; status returned in DX
TEST DX, ISDEV
JZ DP_LX ; bit not set: file
MOV DEFER_DSPL, OFF ;
TEST DX, ISCOT
JZ DP_LX
CMP ANSI, ON ; ANSI or BIOS cursor handling ?
JE DP_L1
;----- get video state
;
MOV AH, 0FH
INT 10H
MOV MAX_COL, AH
MOV VIDEO_PAGE, BH
;----- get cursor position and attributes
;
MOV AH, 03 ; BH still contains video page number
INT 10H ; get current cursor
MOV CURSOR_TYPE, CX ; save cursor data
JMPS DP_L2
DP_L1:
LEA SI, ANSI_GET_CURSOR ; get cursor position through
CALL PRINT ; ANSI-sequence <esc>[6n
CALL ANSI_REPLY ; get ANSI-reply
DP_L2:
MOV CURSOR_POS, DX ; initialize cursor variables
MOV STR_START_POS, DX ;
MOV STR_END_POS, DX ;
MOV FULL_EDIT, ON ;
DP_LX:
POP BX
POP DX
RET
DEVICE_PROLOGUE ENDP
DEVICE_EPILOGUE PROC NEAR
;-----------------------------------------------------
; in: SI = pointer to start of string
; If DEFER_DSPL is ON then now is the time to display
; the completed current string.
; A final carriage return is sent to standard output
; in all cases.
;
MOV INSERT_TOGGLE, OFF
CALL SET_CURSOR_TYPE
CMP DEFER_DSPL, ON
MOV DEFER_DSPL, OFF ; tell DISPLAY_COMPLEX to no longer
; postpone display
JNE DE_L2
;----- must send string to STDOUT now
CLR CH
MOV CL, [SI-1] ; load length of string
JCXZ DE_L2
CLD
DE_L1:
LODSB
CALL DISPLAY_COMPLEX
LOOP DE_L1
DE_L2:
MOV AL, CR
CALL DISPLAY_CHAR
RET
DEVICE_EPILOGUE ENDP
SET_CURSOR_POS PROC NEAR
;-----------------------------------------------
; If FULL_EDIT is ON then the cursor position
; on the screen is updated according to the value
; in CURSOR_POS.
; If ANSI is ON then cursor-positioning is done
; through ANSI-escape-sequences, else through
; BIOS video-routines.
;
PUSH DX
PUSH BX
PUSH AX
CMP FULL_EDIT, ON
JNE SCP_LX
MOV DX, CURSOR_POS
CMP ANSI, ON
JE SCP_L1
;----- cursor positioning through BIOS
; function no. 2, DX = cursor-pos
;
MOV BH, VIDEO_PAGE
MOV AH, 2
INT 10H
JMPS SCP_LX
SCP_L1:
;----- cursor positioning through ANSI
; output ANSI-sequence <esc>[<col>;<row>f
; Note: ANSI row- and column-numbers are
; in the range: 1 .. MaxRow/MaxCol
;
MOV AL, ESCAPE
CALL WRITE_CHAR ; <esc>
MOV AL, '['
CALL WRITE_CHAR ; [
MOV AL, DH ; print row number
INC AL
CLR AH ;
CALL PRINT_NUM ;
MOV AL, ';' ; ;
CALL WRITE_CHAR
MOV AL, DL ; print column number
INC AL
CLR AH ;
CALL PRINT_NUM ;
MOV AL, 'f' ; f
CALL WRITE_CHAR
SCP_LX:
POP AX
POP BX
POP DX
RET
SET_CURSOR_POS ENDP
SET_CURSOR_TYPE PROC NEAR
;----------------------------------------------------------------
; Set cursor shape to value in variable CURSOR_TYPE.
; This can only be done through BIOS-VIDEO call so
; full edit mode must be ON and ANSI mode OFF
;
CMP FULL_EDIT, ON
JNE SCT_L2
CMP ANSI, OFF ; no cursor type change
JNE SCT_L2 ; if ANSI on
PUSH CX
PUSH AX
MOV CX, CURSOR_TYPE ; set proper cursor type
CMP INSERT_TOGGLE, ON
JNE SCT_L1 ; if insert-state
SUB CH, INSERT_SHIFT ; make larger cursor
SCT_L1:
AND CH, CURSOR_MASK
MOV AH, 01
INT 10H
POP AX
POP CX
SCT_L2:
RET
SET_CURSOR_TYPE ENDP
ANSI_REPLY PROC NEAR
;-------------------------------
; get reply from ANSI-driver
; must be of the form <esc>[#;#R<cr>
; out: DX=#;#
;
PUSH CX
PUSH BX
GET_CHAR ;
CMP AL, ESCAPE ; <esc> ?
JNE AR_LX
GET_CHAR
CMP AL, '[' ; [
JNE AR_LX
MOV BL, ';' ; get first # (row)
CLR CX
AR_L1: ; CX = intermediate number
GET_CHAR
CMP AL, BL ;
JE AR_L3 ;
CMP AL, '9' ; read number and convert
JA AR_LX ;
SUB AL, '0' ;
JB AR_LX ;
XCHG CX, AX
PUSH DX
MUL TEN
POP DX
JNC AR_L2 ; test for overflow
OR AX, TEN ; do something to reset Z-flag
JMPS AR_LX
AR_L2:
CLR CH ; add new digit
ADD AX, CX
XCHG CX, AX
JMP AR_L1
AR_L3:
DEC CL ; 1 .. Max --> 0 .. Max-1
CMP BL, 'R' ; read second # (column)
JE AR_L4
MOV DH, CL
MOV BL, 'R'
CLR CX
JMP AR_L1
AR_L4:
MOV DL, CL
AR_LX:
GET_CHAR ; wait for Carriage Return
CMP AL, CR ;
JNE AR_LX ;
POP BX
POP CX
RET
ANSI_REPLY ENDP
SECOND_CODE_TABLE LABEL BYTE
;----- table of extended ascii-codes
;
DB INSERT
DB DELETE
DB CURSOR_LEFT
DB CURSOR_RIGHT
DB PREV_LINE
DB NEXT_LINE
DB CURSOR_BEGIN
DB CURSOR_END
DB F1
DB F2
DB F3
DB F4
DB F5
DB F6
DB F7
DB CTRL_LEFT
DB CTRL_RIGHT
DB CTRL_HOME
DB CTRL_END
DB CTRL_PGUP
DB CTRL_PGDN
DB SHIFT_F5
DB SHIFT_F6
DB ALT_1
DB ALT_2
DB ALT_3
DB ALT_4
DB ALT_5
DB ALT_6
DB ALT_7
DB ALT_8
DB ALT_9
DB ALT_0
DB 00 ; end of table
CODE_TABLE_LENGTH = $ - OFFSET SECOND_CODE_TABLE
;----- ECJT = Extended Code Jump Table
;
ECJT LABEL WORD
DW MAIN_LOOP_RETURN
DW ALT_DIGIT ; get old string
DW ALT_DIGIT
DW ALT_DIGIT
DW ALT_DIGIT
DW ALT_DIGIT
DW ALT_DIGIT
DW ALT_DIGIT
DW ALT_DIGIT
DW ALT_DIGIT
DW ALT_DIGIT
DW SHIFT_F6_R ; cursor shape control
DW SHIFT_F5_R ; for INSERT-mode
DW CTRL_PGDN_R ; cursor-bottom up
DW CTRL_PGUP_R ; cursor-top up
DW CTRL_END_R ; cursor-bottom down
DW CTRL_HOME_R ; cursor-top down
DW CTRL_RIGHT_R ; cursor up
DW CTRL_LEFT_R ; cursor down
DW F7_R ; zero-byte in string
DW F6_R ; control-Z in string
DW F5_R ; add string to buffer
DW F4_R ; erase from begin to cursor
DW F3_R ; erase from cursor to end
DW F2_R ; word right
DW F1_R ; word left
DW CURSOR_END_R
DW CURSOR_BEGIN_R
DW NEXT_LINE_R
DW PREV_LINE_R
DW CURSOR_RIGHT_R
DW CURSOR_LEFT_R
DW DELETE_R
DW INSERT_R
PAGE
;#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-
;#
;# *** Data structure to maintain a buffer of previous strings ***
;#
;#
;# ___/------ STR_BUF_START
;# C_TBL_START ------\ _________ |_x_|
;# |_________| |_x_|
;# |_________| |_x_|
;# |_________| |_x_|
;# C_TBL_HEAD ------>|_________|-------\ |_x_|
;# |_________|----\ \ |_x_|
;# |_________| \ \-------> |_x_|
;# |_________| \ |_y |
;# |_________| \ |_y |
;# C_TBL_TAIL ------>|_________|-- \ \ |_y_|
;# |_________| \ \ |_y_|
;# |_________| \ \ |_y |
;# C_TBL_END ---------/ \ \-----> |_y_|
;# \ ~ ~
;# C-table \ ~_ _~
;# \ |_z_|
;# \ |_z_|
;# \ |_z_|
;# \----> |_z_|
;# |___|
;# |___|
;# |___|
;# \------- STR_BUF_END
;#
;# string-buffer
;#
;#
;# The entries in C_TABLE point to the last character of a string
;# in STR_BUF.
;#
;# C_TBL_HEAD points to the entry in C-table which identifies the oldest
;# string in the string-buffer.
;#
;# C_TBL_TAIL points to the entry in C-table which points to the last
;# byte of free space in the string-buffer.
;#
;#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-
STR_BUF_SIZE DW 512 ; default string-buffer
; size is 512
STR_BUF_START DW ?
STR_BUF_END DW ?
;----- pointers to mark begin and end of data in the string buffer
;
STR_BUF_HEAD DW ?
STR_BUF_TAIL DW ?
;----- table of pointers to strings in string buffer
;
; pointers in string pointer table to locate
; the string currently being processed
;
; C_TBL_HEAD identifies the oldest string in STR_BUF
; C_TBL_TAIL points to the last free byte in STR_BUF, i.e. the byte
; before the begin of the string pointed to by C_TBL_HEAD
;
C_TBL_SIZE DW 60 ; default 30 commands
C_TBL_START DW ?
C_TBL_END DW ?
C_TBL_HEAD DW ?
C_TBL_TAIL DW ?
CURRENT DW ? ; identifies the string current-
; ly being edited
;************************************************************
; Next are a couple of routines to handle pointer-
; updating for the cyclic buffers defined above.
; Pointers are passed in the BX register
; BX is advanced to the next or previous table-item.
;************************************************************
SCROLLUP_TBL PROC NEAR
CMP BX, C_TBL_START ; at start of table?
JNE SUT_L1 ; no: then go update pointer
MOV BX, C_TBL_END ; yes: scroll to end of
; table first
SUT_L1:
DEC BX ;
DEC BX ; pointer to previous entry
RET
SCROLLUP_TBL ENDP
SCROLLDO_TBL PROC NEAR
INC BX ; pointer to next entry
INC BX ;
CMP BX, C_TBL_END ; past end?
JNE SDT_L1 ; no: all is well
MOV BX, C_TBL_START ; yes: back to start of table
SDT_L1:
RET
SCROLLDO_TBL ENDP
BEEP PROC NEAR
;----------------------------------------------
; Buffer full/error beep
;
PUSH CX
PUSH AX
MOV AL, 10010110B ; select Timer 2, low order Count
OUT 43H, AL
CLR AL ; set low order Count
JMP $+2 ; allow for AT pecularities
OUT 42H, AL ; (slow 8053-chip)
MOV AL, 10100110B ; select Timer 2, high order Count
OUT 43H, AL
MOV AL, 3 ; pitch
JMP $+2
OUT 42H, AL ; set high order Count
IN AL, 61H
MOV AH, AL ; save value
OR AL, 03H ; turn speaker on
JMP $+2
OUT 61H, AL
MOV CX, 20000 ; beep length
BRRR:
LOOP BRRR
MOV AL, AH ; restore old value
OUT 61H, AL
POP AX
POP CX
RET
BEEP ENDP
WRITE_CHAR PROC NEAR
;------------------------------------------
; Display character in AL via OS call 2
; or through BIOS TTY routine 0EH
; (necessary at initialization)
;
PUSH AX
CMP BIOS_TTY_SW, ON
JNE W_CH_L1
MOV AH, 0EH ; TTY-command
INT 10H ; video-BIOS-routine
JMPS W_CH_LX
W_CH_L1:
PUSH DX
MOV DL, AL ; deliver in DL
MOV AH, 2 ; function number
INT 21H
POP DX
W_CH_LX:
POP AX
RET
WRITE_CHAR ENDP
PRINT PROC NEAR
;-------------------------------------------------------------
; This routine displays a string a string of characters on
; the crt-screen.
; The string is assumed to start at address DS:SI
; and must end with a null byte.
;
CLD
PRINT_L1:
LODS BYTE PTR CS:[SI]
OR AL, AL ; load bytes until
JZ PRINT_L2 ; a zero is read
CALL WRITE_CHAR
JMP PRINT_L1
PRINT_L2:
RET
PRINT ENDP
PRINT_NUM PROC NEAR
;---------------------------------------
; Convert to ascii and display number
; in: AX: unsigned integer
;
;
PUSH DX
PUSH CX
PUSH BX
MOV BX, 10 ; BX = divisor
CLR CX ; CX = digit count
PR_N_L1:
CLR DX ; DX:AX = dividend
DIV BX
PUSH DX ; remainder on stack
INC CX ; keep count
OR AX, AX ; quotient = 0 ?
JNE PR_N_L1 ; if not, compute next digit
PR_N_L2:
POP AX ; get digits from stack
ADD AL, '0' ; convert to ascii
CALL WRITE_CHAR ; and display
LOOP PR_N_L2 ;
POP BX
POP CX
POP DX
RET
PRINT_NUM ENDP
PAGE
DISPLAY_CHAR PROC NEAR
;---------------------------------------------------------------
; Display character in AL.
; Update cursor data.
;
CALL WRITE_CHAR
CMP AL, CR
JNE DCH_L1
;----- carriage return
;
MOV CURSOR_COL, 0
RET
DCH_L1:
CMP AL, LF
JNE DCH_L3
;----- line feed
;
CMP CURSOR_ROW, MAX_ROW - 1
JE DCH_L2
INC CURSOR_ROW
DCH_L2:
RET
DCH_L3:
CMP AL, BACKSPACE
JNE DCH_L5
;----- backspace
;
DEC CURSOR_COL ; cursor one left
JNS DCH_L4 ; if < 0
MOV AL, MAX_COL ; wrap back to previous row
ADD CURSOR_COL, AL ;
DEC CURSOR_ROW
CALL SET_CURSOR_POS ; must set cursor explicitly
DCH_L4: ; for wrap back
RET
DCH_L5:
;----- other characters
;
MOV AX, STR_END_POS ; AX := difference between
SUB AX, CURSOR_POS ; STR_END and CURRENT
PUSH AX
INC CURSOR_COL
MOV AL, MAX_COL ; increment column count
CMP CURSOR_COL, AL ; if at end move to next line
JB DCH_L6 ;
MOV CURSOR_COL, 0 ;
CMP CURSOR_ROW, MAX_ROW - 1 ; on last row ?
JE DCH_L6 ; yes, don't increment ROW
INC CURSOR_ROW ; because of scrolling
DCH_L6:
POP AX
OR AX, AX ; update STR_END_POS
JNZ DCH_L7 ; if necessary
MOV AX, CURSOR_POS
MOV STR_END_POS, AX
DCH_L7:
RET
DISPLAY_CHAR ENDP
DISPLAY_COMPLEX PROC NEAR
;**************************************************************
;* This procedure takes an ascii-code looks for control-code
;* and displays it on the screen in the proper format
;* in: AL : ascii-code
;*
;* Irregular service for BS and TAB
;*
;* If DEFER_DSPL is ON then display of the string being edited
;* is deferred until editing is complete.
;***************************************************************
CMP DEFER_DSPL, ON
JE DC_X
CMP AL, 20H ; is it a control character ?
JNB DC_NO_CTRL ;
CMP AL, BACKSPACE ; control characters,
JE DC_NO_CTRL ; handle BS and TAB different
CMP AL, TAB ;
JNE DC_L2 ;
;----- display TAB character, ie. display spaces until
; current column number = 0 (MOD 8)
;
PUSH CX
CLR CH
MOV CL, CURSOR_COL ;
OR CL, 0F8H ; CL := 8 - (CURSOR_COL MOD 8)
NEG CL ;
DC_L1:
MOV AL, ' '
CALL DISPLAY_CHAR
LOOP DC_L1
POP CX
JMPS DC_X
;----- display control character other then TAB or BS
; first display a caret, then the corresponding capital
; ie. ascii 1 displays as: '^A'
;
DC_L2:
PUSH AX
MOV AL, '^' ; display ascii-control-character
CALL DISPLAY_CHAR
POP AX
OR AL, 40H ; make it readable
DC_NO_CTRL:
CALL DISPLAY_CHAR
DC_X:
RET
DISPLAY_COMPLEX ENDP
DISPLAY_TAIL PROC NEAR
;*************************************************
;* this routine displays the substring starting
;* at the character pointed to by DI, to the end
;* of the buffer
;* in: CX: number of blanks to display after the
;* string to blank out any remnant characters
;*************************************************
PUSH CURSOR_POS ; save current screen position
PUSH CX
MOV CL, BL ; get length of substring
SUB CL, BH ; in CX
CLR CH
JCXZ DT_L2 ; display specified blanks if
; the substring is empty
MOV SI, DI
DT_L1:
LODSB ; get character to display
CALL DISPLAY_COMPLEX
LOOP DT_L1
DT_L2:
POP CX ; recover number of additional blanks
JCXZ DT_L4
DT_L3:
MOV AL, ' ' ;
CALL DISPLAY_COMPLEX ;
LOOP DT_L3
DT_L4:
POP CX ; recover screen position
MOV AL, CH ; AL := (OLD)STR_END_ROW
SUB CX, CURSOR_POS ;
NEG CX ; !! CX := CURSOR_POS - (OLD)STR_END_POS
SUB AL, CURSOR_ROW
NEG AL ; AL := CURSOR_ROW - (OLD)STR_END_ROW
MOV AH, MAX_COL ; sofar we pretended each row is 256
NEG AH ; characters long, so now we must
MUL AH ; compensate for this by subtracting
SUB CX, AX ; (256 - characters/column)*ROW_diff.
;
JCXZ DT_L6
DT_L5:
MOV AL, BACKSPACE ; use as backspace count
CALL DISPLAY_COMPLEX ; move cursor back
LOOP DT_L5
DT_L6:
RET
DISPLAY_TAIL ENDP
MOVE_TAIL_LEFT PROC NEAR
;***************************************************
;* routine to move the substring starting right of
;* the current cursor position, pointed to by DI,
;* one position to the left, overwriting the current
;* cursor position
;* none of the pointers are updated
;***************************************************
PUSH CX
PUSH DI
CLR CH ;
MOV CL, BL ; get length of substring
SUB CL, BH ; in CX
DEC CX
JNA MTL_LX ; test for non-empty substring
MOV SI, DI ;
INC SI ; SI at first item to move
CLD ; count forward
REP MOVSB ;
MTL_LX:
POP DI
POP CX
RET
MOVE_TAIL_LEFT ENDP
MOVE_TAIL_RIGHT PROC NEAR
;***************************************************
;* routine to move the substring starting at
;* the current cursor position, pointed to by DI,
;* one position to the right.
;* none of the pointers are updated
;***************************************************
PUSH CX
PUSH DI
CLR CH ;
MOV CL, BL ; length of substring in CX
SUB CL, BH ;
JCXZ MTR_LX ; no business for empty string
ADD DI, CX ; DI one past end string
MOV SI, DI
DEC SI ; SI at end string
STD ; count backward
REP MOVSB ;
MTR_LX:
POP DI
POP CX
RET
MOVE_TAIL_RIGHT ENDP
BACK_TAB PROC NEAR
;************************************************************
;* Compute number of backspaces needed to remove a TAB
;* at the current cursor position.
;* It is assumed that string pointers are consistent
;* i.e. DI points to position of TAB to be removed
;* and BL, BH give length of whole string and prefix resp.
;* required number = 8 - (current screen colunm MOD 8)
;*
;* out: CL: equivalent number of spaces for TAB in question
;************************************************************
PUSH DI ; save current string index
PUSH DX ; save string length
DEC DI ; position on previous character
CLR CH
MOV CL, BH ; CX := prefix length
MOV DH, 07H
MOV AL, 20H
JCXZ BT_3
STD ; scan downwards through prefix
BT_1:
SCASB
JBE BT_2 ; control character ?
CMP BYTE PTR ES:[DI+1], TAB ;
JE BT_4 ; if tab then exit loop (we certainly are
; at screen position which equals 0 (MOD 8) )
DEC DH
BT_2:
LOOP BT_1
; here: CL = length to penultimate tab
; or zero (if no tab)
BT_3: ; and: DH = - (no of carets)
SUB DH, STR_START_COL ; DH := - carets - STR_START_COL
BT_4:
SUB DH, BH ; DH := - carets {- STR_START_COL}
; - total
ADD CL, DH ; CL := - carets {- STR_START_COL}
; - (length since penultimate tab)
AND CL, 07H ; CL := 8 - ((-CL) MOD 8)
INC CL ;
CLD ; reset direction flag
POP DX ; restore string length
POP DI ; and string index
RET
BACK_TAB ENDP
PAGE
;-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
;
; K E Y B O A R D E D I T O R ( ENTRY POINT )
;
;-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
FUNCTION_INT_ENTRY PROC
;****************************************************************
;* the address of this routine is placed in the interrupt vector
;* to intercept interrupt 21H. Function call 0AH is handled here,
;* others are passed on to DOS
;*
;****************************************************************
CMP EDT_INST, OFF
JZ FI_NOT_ACTIVE
CMP AH, 0AH
JZ FI_ENTRY
FI_NOT_ACTIVE:
;----- go on to DOS-routines
;
JMP CS: OLD_FUNCTION_INT
FI_ENTRY:
STI
PUSH ES ;
PUSH DS ;
PUSH BP ;
PUSH DI ;
PUSH SI ; save registers
PUSH DX ;
PUSH CX ;
PUSH BX ;
PUSH AX
CALL SETSTACK ; use own stack
CALL DEVICE_PROLOGUE
MOV AX, DS ; make ES point to buffer-
MOV ES, AX ; segment also
MOV SI, DX
CLD
LODSW ; get length of the passed buffer
PUSH SI ; save address of start of actual string
OR AL, AL ; if length = zero return
JZ FI_EXIT
MOV DL, AL ; DL will contain the string's
DEC DL ; maximum length, without the
; final Carriage Return
MOV CL, AH
CLR CH
MOV BX, CX
JCXZ FI_RESTART
CMP BYTE PTR DS:[SI+BX], CR ; is input-string
; terminated properly ?
JNE FI_RESTART ; no, ignore
CALL STR_BUF_INSERT
FI_RESTART:
MOV DH, STR_START_COL ; initialize column counter
MOV CURSOR_COL, DH
CALL SET_CURSOR_POS
MOV INSERT_TOGGLE, OFF ; overwrite mode
CALL SET_CURSOR_TYPE
CLR BL ; BL will contain the current number
; of characters in the string
CLR BH ; BH will contain the number of
; characters up to but not including
; the cursor position
POP DI ; DI is the current position in the
; buffer
PUSH DI ; address back on stack
GET_CHAR ; get first character
CMP AL, LF ; if first character is a
JE MAIN_LOOP ; line-feed then throw it away
JMP MAIN_ENTRY ; enter main loop at main
; entrance-point
FI_EXIT:
POP SI
CALL DEVICE_EPILOGUE
CALL RESETSTACK ; restore user stack
POP AX ;
POP BX ;
POP CX ;
POP DX ;
POP SI ; restore registers
POP DI ;
POP BP ;
POP DS ;
POP ES ;
IRET
FUNCTION_INT_ENTRY ENDP
MAIN_LOOP PROC NEAR
GET_CHAR
MAIN_ENTRY:
CMP AL, ACK
JE MAIN_LOOP ; no ACK's
LEA BP, MAIN_LOOP ; set up return address on
PUSH BP ; the stack for all routines
; wishing to return to MAIN_LOOP
CMP AL, LF
JE LF_RN ;
CMP AL, BACKSPACE ; find out whether one
JE BACKSPACE_RN ; of these special
; CMP AL, ESCAPE ; characters was read
; JE ESCAPE_RN ;
CMP AL, CTRL_U
JE ESCAPE_RN
CMP AL, CR ; search for editing
JE CR_R ; keys
CMP AL, CTRL_W
JE CW_RN
CMP AL, 0 ;
JE EXTENDED_ASCIIN ;
;----- a character must be inserted in the current string
;
M_L1:
CMP INSERT_TOGGLE, ON ; insert- or
JNE OVERWRITE ; overwrite-mode?
CMP BL, DL ; is buffer full ?
JB M_L2 ; no: put this character
CALL BEEP ; in the buffer
RET ; else: ring the bell
M_L2:
CALL MOVE_TAIL_RIGHT ; make room for new character
CLD ;
STOSB ; insert the new character
INC BL ; update length
INC BH ; and cursor position
CLR CX
;---- display updated string
; also postlude for OVERWRITE routine
;
M_L3:
CALL DISPLAY_COMPLEX ; display the new character
CALL DISPLAY_TAIL ; and those to the right of it
M_L4:
RET
OVERWRITE:
CMP BH, BL ; if cursor position
JB M_L6 ; at end, then check for
CMP BL, DL ; full buffer
JB M_L5 ;
CALL BEEP ;
RET ;
M_L5: ;
INC BL
M_L6: ; if a character is added at
INC BH ; the end, update total
; length also
CLR CX
CMP BH, BL ; at end of string ?
JE M_L8 ; yes, just add it
;----- must overwrite some character
; get character to overwrite in AH
; character to overwrite with still in AL
MOV AH, ES:[DI]
CMP AH, TAB ; is it a TAB ?
JNE M_L7 ; go if not
;----- get rid of TAB
;
PUSH AX
DEC BH ; BACKTAB expects string
CALL BACK_TAB ; pointers set up for tab prefix
INC BH ; restore prefix length
DEC CX
POP AX
JMPS M_L8
M_L7:
CMP AH, 20H ; if control character
JAE M_L8 ; then check character to write
INC CX
CMP AL, TAB ; tab can also show as one space
JE M_L8
CMP AL, 20H
JNB M_L8 ; if not cntr-char
DEC CX ; display extra space
;----- character to buffer
;
M_L8:
CLD
STOSB
JMP M_L3 ; go update screen
;----- boost some conditional jumps
;
LF_RN: JMP LF_R
BACKSPACE_RN: JMP BACKSPACE_R
ESCAPE_RN: JMP ESCAPE_R
CW_RN: JMP CTRL_W_R
EXTENDED_ASCIIN:JMP EXTENDED_ASCII
CR_R:
;----------------------------
; add a Carriage Return to the end of the string, set string-length byte
; insert in STR_BUF, and go to exit
; ! the carriage return character is sent to STDOUT in DEVICE_EPILOGUE
;
CLR CH ;
MOV CL, BL ; set DI to end of string
SUB CL, BH ;
ADD DI, CX ;
STOSB ; and put carriage return there
PUSH STR_END_POS
POP CURSOR_POS
CALL SET_CURSOR_POS
POP BP ; remove return address from stack
POP SI ; recover first-byte address
PUSH SI
LEA BP, FI_EXIT
PUSH BP
MOV [SI-1], BL ; store count
CLR CH
MOV CL, BL ; length in CX
STR_BUF_INSERT:
;-----------------------------------------------------
; On entry CX contains the length of the string
; and SI points to the string's first byte.
; note: this code is called as a subroutine at entry
; and by F5_R
; When freeing space to insert a new string, as many of
; the oldest strings are removed as is necessary to make
; room for the new one. Because maximum string length is
; is 255 and minimum length of strings in the buffer is 1
; it follows that no more than 255 should ever be removed.
; Therefore, a counter is maintained during the the loop
; which removes old strings from the buffer to prevent an
; accidentally corrupted data-structure from causing an
; endless loop here.
;
JCXZ SBI_L17 ; if string-length = 0 don't insert
;----- load register DI with address of last byte before
; begin of free space in STR_BUF
;
MOV BX, C_TBL_TAIL ;
MOV CURRENT, BX ; must update CURRENT
CMP BX, C_TBL_HEAD
JE SBI_L1
CALL SCROLLUP_TBL
SBI_L1:
MOV DI, CS:[BX]
JE SBI_L18 ; if table empty, we should not enter
; compare routine
; ! proc SCROLLUP does not set Z-flag
;----- determine whether string is different from previous
;
PUSH DI
MOV AX, DI
CMP BX, C_TBL_HEAD ; wrap pointers if appropriate
JNE SBI_L11 ;
MOV BX, C_TBL_TAIL ;
JMPS SBI_L12
SBI_L11:
CALL SCROLLUP_TBL
SBI_L12:
MOV DI, CS:[BX]
SUB AX, DI ; compute length of previous string
JAE SBI_L13 ;
ADD AX, STR_BUF_SIZE; string can wrap in STR_BUF
SBI_L13:
CMP AX, CX ; compare lengths
JNE SBI_L16 ; go if not equal
PUSH ES ; save registers altered
PUSH SI ; when comparing strings
PUSH CX ;
MOV AX, CS ; set up addressing
MOV ES, AX ;
CLD ;
INC DI ;
SBI_L14:
CMP DI, STR_BUF_END ; compare string in
JNE SBI_L15 ; STR_BUF with input string
MOV DI, STR_BUF_START ;
;
SBI_L15: ;
CMPSB ;
LOOPZ SBI_L14 ;
POP CX ; restore registers
POP SI ;
POP ES ;
SBI_L16:
POP DI
JNE SBI_L18
SBI_L17:
RET ; return if strings proved equal
SBI_L18:
;----- next get an entry in C_TBL for the new string
;
MOV BX, C_TBL_TAIL ; C_TBL_TAIL is moved to make room,
MOV AX, CS:[BX] ; save its contents first, ...
PUSH DI ;
ADD DI, CX ; calculate where new string
CMP DI, STR_BUF_END ; will end in STR_BUF,
JB SBI_L2 ;
SUB DI, STR_BUF_SIZE
SBI_L2:
MOV CS:[BX], DI ; ... then fill it with new length
POP DI
CLR DH ; use as retry count for loop
; starting at label SBI_L3
CALL SCROLLDO_TBL ;
MOV C_TBL_TAIL, BX ; new tail
MOV CURRENT, BX ; and new current also
CMP BX, C_TBL_HEAD ; collision with head ?
JNE SBI_L3 ; no, go and restore contents
PUSH BX ; yes, remove oldest string
CALL SCROLLDO_TBL ; space is automatically deallocated
MOV C_TBL_HEAD, BX ; by moving the pointers
POP BX ; BX back at tail
MOV AX, CS:[BX] ; new value for end of free space
SBI_L3:
MOV CS:[BX], AX ; set new C_TBL_TAIL to current
; end of free space
;----- determine if there is enough free space to hold the new
; string and keep removing old entries until there is
;
SUB AX, DI ; AX contains current end of free space
JA SBI_L4 ; AX=DI means STR_BUF is empty (free-
; pointer = last-string-pointer)
ADD AX, STR_BUF_SIZE; undo wrap-around effect
SBI_L4:
CMP AX, CX ; is there enough ?
JA SBI_L5 ; yes, go and do the transfer
; note: always keep one byte free
DEC DH ; how many times did we try ?
JZ SBI_ERR ; if zero too many: something is wrong
; exit with beep, also RESET is called
PUSH BX ; no, remove another entry from TABLE
MOV BX, C_TBL_HEAD
MOV AX, CS:[BX] ; get address of last byte occupied
CALL SCROLLDO_TBL ; by this entry's string
MOV C_TBL_HEAD, BX ; new head of the table
POP BX ;
JMP SBI_L3 ; try again
;----- now we are ready for the transfer
;
SBI_L5:
MOV AX, CS ;
MOV ES, AX ; set up target pointers
INC DI ;
CLD ; count in right direction
SBI_L6:
CMP DI, STR_BUF_END ;
JNE SBI_L7 ; wrap-around if neccesary
MOV DI, STR_BUF_START
SBI_L7:
MOVSB ; store bytes of string
LOOP SBI_L6 ; until done (CX=0)
MOV AX, DS ;
MOV ES, AX ; restore ES
SBI_LX:
RET
SBI_ERR:
CALL RESET_EDT
CALL BEEP
RET
LF_R:
CALL CURSOR_END_R
MOV AL, LF
CALL DISPLAY_CHAR
MOV AL, CR
CALL DISPLAY_CHAR
RET
BACKSPACE_R:
OR BH, BH ; is cursor at begin of string ?
JZ BS_L1 ; yes, then nothing to do
CALL CURSOR_LEFT_R ; otherwise, delete the character
CALL DELETE_R ; left of the current cursor-position
BS_L1: RET
;---------------------------------
; abandon current string and start editing a new one
ESCAPE_R: CALL CURSOR_BEGIN_R
CALL F4_R
CALL CURSOR_END_R
ADD SP, 2 ; discard return address on stack
JMP FI_RESTART ; redo from scratch
; Delete previous whitespace, or alphanumerics
CTRL_W_R: CALL BACKSPACE_R ; delete a character in any case
OR BH, BH ; at beginning of string?
JZ CW_RET ; yes, done
CMP BYTE PTR [DI-1],' ' ; tab or...
JZ CTRL_W_BL
CMP BYTE PTR [DI-1],' ' ; blank or...
JZ CTRL_W_BL
CTRL_W_LOOP: CMP BYTE PTR [DI-1],'0' ; numeric
JB CW_RET
CMP BYTE PTR [DI-1],'9' ; ...
JBE CTRL_W_NB
CMP BYTE PTR [DI-1],'A' ; or alpha, continue deletion
JB CW_RET
CMP BYTE PTR [DI-1],'Z'
JBE CTRL_W_NB
CMP BYTE PTR [DI-1],'a'
JB CW_RET
CMP BYTE PTR [DI-1],'z'
JBE CTRL_W_NB
CW_RET: RET
CTRL_W_BL: CALL BACKSPACE_R ; Flush previous character
OR BH,BH ; at beginning?
JZ CW_RET ; Yes...
CMP BYTE PTR [DI-1],' ' ; tab or...
JZ CTRL_W_BL
CMP BYTE PTR [DI-1],' ' ; blank or...
JZ CTRL_W_BL
JMP CW_RET
CTRL_W_NB: CALL BACKSPACE_R ; delete a character in any case
OR BH, BH ; at beginning of string?
JZ CW_RET ; yes, done
JMP CTRL_W_LOOP
EXTENDED_ASCII:
GET_CHAR
PUSH ES ; the second code is in AL now
PUSH DI
MOV DI, CS
MOV ES, DI
LEA DI, SECOND_CODE_TABLE
MOV CX, CODE_TABLE_LENGTH
CLD
CLI ; we need CX after scan
REPNE SCASB ; search for extended code in table
STI
SHL CX, 1 ; CX is now offset in jump-table
MOV BP, CX
POP DI
POP ES
JMP [BP+ECJT]
INSERT_R:
;----- flip the insert-toggle
;
NOT INSERT_TOGGLE
CALL SET_CURSOR_TYPE
RET
DELETE_R:
;----- delete the character under the cursor
;
CMP BL, BH ; if cursor at end
JE DR_L2 ; then nothing to delete
MOV AH, ES:[DI] ; remember victim for display update
CALL MOVE_TAIL_LEFT ; remove from buffer
DEC BL ; update total length
;----- next update display
;
MOV CX, 1 ; at least one character to erase
CMP AH, 20H ; control character ?
JAE DR_L1 ;
INC CX ; yes, erase the caret too
CMP AH, TAB ; beware of the TAB
JNE DR_L1 ; if TAB then some calculation required
; to determine shift of display
CALL BACK_TAB
DR_L1:
CALL DISPLAY_TAIL
DR_L2:
RET
CURSOR_LEFT_R:
;----- move current cursor position one to the left
;
OR BH, BH
JZ CL_ATBEGIN ; cursor at begin of string
DEC DI ; update pointers
DEC BH ;
;----- update display
;
MOV CX, 1 ; set up for one backspace
MOV AH, [DI] ; get character under cursor
CMP AH, 20H ; if control-character then
JAE CL_BS ; two backspaces are required
INC CL ; if ctrl-char then two backspaces
CMP AH, TAB ; exception: tab-character
JNE CL_BS ;
CALL BACK_TAB ; compute number of backspaces
; needed for this tab (returned in CL;)
CL_BS:
MOV AL, BACKSPACE
CALL DISPLAY_COMPLEX
LOOP CL_BS
RET
CL_ATBEGIN:
CALL BEEP
RET
CURSOR_RIGHT_R:
;----- move current cursor position one to the right
;
CMP BL, BH
JE CR_AT_END ; cursor at end of string
MOV AL, [DI] ; get character under cursor
INC DI ; update pointers
INC BH ;
;----- update display
;
CMP AL, 20H
JB CRR_L2
;----- non-control ascii character, so
; cursor must move right one position
; see if this can be done by cursor control
; if not then redisplay character
;
CMP FULL_EDIT, ON
JNE CRR_L2
MOV AX, CURSOR_POS
INC AL
CMP AL, MAX_COL
JB CRR_L1
CLR AL
CMP AH, MAX_ROW - 1
JE CRR_L1
INC AH
CRR_L1:
MOV CURSOR_POS, AX
CALL SET_CURSOR_POS
RET
CRR_L2:
;----- move cursor right by redisplaying the character
; which is under cursor now
;
CALL DISPLAY_COMPLEX
RET
CR_AT_END:
CALL BEEP
RET
PREV_LINE_R:
;------------------------------
; empty the current buffer and get the one less
; recent string from the string buffer
MOV BP, SP ; need access to buffer address
; which is saved on the stack
PUSH STR_END_POS ; save current end of string on screen
CALL CURSOR_BEGIN_R
;----- next find a new string to be edited
;
MOV AX, C_TBL_HEAD ; take a look in the
CMP AX, C_TBL_TAIL ; string pointer table
JE PNL_EMPTY ; no string there
; proceed with empty string
MOV BX, CURRENT ;
CMP BX, AX ; determine previous entry
JNE PL_L1 ; in TABLE,
MOV BX, C_TBL_TAIL ;
PL_L1:
CALL SCROLLUP_TBL ;
MOV CURRENT, BX ; and make it the current one
MOV CX, CS:[BX] ;
CMP BX, AX ; determine length of this
JNE PL_L2 ; candidate by scrolling up
MOV BX, C_TBL_TAIL ; another entry ...
JMPS PL_L3 ;
PL_L2:
CALL SCROLLUP_TBL ;
PL_L3: ;
MOV SI, CS:[BX] ; ... and subtracting the
PNL_L1:
SUB CX, SI ; addresses of the strings'
JA PNL_L2 ; last bytes.
ADD CX, STR_BUF_SIZE; we have wrapped around in STR_BUF
PNL_L2:
JCXZ PNL_EMPTY
MOV BL, CL ; CL now contains the candidate's
MOV BH, CL ; length.
CMP DL, CL ; initialize string-pointers
JNB PNL_L3
CALL BEEP ; if too long ring bell twice (as long)
CALL BEEP
PNL_EMPTY:
ADD SP, 4 ; discard return address and
; EOS which are on the stack
JMP FI_RESTART
;----- the new string can be transferred now
;
PNL_L3:
MOV DI, [BP+2] ; get address of target buffer
INC SI ; set source pointer
PNL_L4:
CMP SI, STR_BUF_END ; wrap-around in STR_BUF
JNE PNL_L5 ; if nessecary
MOV SI, STR_BUF_START
PNL_L5:
LODS BYTE PTR CS:[SI]; do the transfer
STOSB ;
CALL DISPLAY_COMPLEX ; display this byte
LOOP PNL_L4 ;
;-----the pointers BH, BL, and DI are properly set up now
; the current cursor-position is at the end of the new string
;
;----- compute difference in string length
;
POP CX ; recover saved STR_END_POS
; of previous string
MOV AL, CH ; AL := (OLD)STR_END_ROW
SUB CX, CURSOR_POS ; !! CX := CURSOR_POS - (OLD)STR_END_POS
JBE PNL_L8 ; new > old ?
SUB AL, CURSOR_ROW ; AL := CURSOR_ROW - (OLD)STR_END_ROW
MOV AH, MAX_COL
NEG AH
MUL AH ; multiply by (256 - characters/column)
SUB CX, AX ; compensate for subtraction !!
; CX := (oldROW - newROW)*MAX_COL +
; (oldCOL - newCOL)
PUSH CX
PNL_L6:
MOV AL, ' ' ; blank out remnant characters
CALL DISPLAY_COMPLEX
LOOP PNL_L6
POP CX
PNL_L7:
MOV AL, BACKSPACE ; set cursor back
CALL DISPLAY_COMPLEX
LOOP PNL_L7
PNL_L8:
PUSH CURSOR_POS
POP STR_END_POS
RET ; top of stack = address MAINLOOP
NEXT_LINE_R:
;---------------------------
; empty the current buffer and get the one more
; recent string from the string buffer
MOV BP, SP ; need access to buffer address
; which is saved on the stack
PUSH STR_END_POS ; save current end of string on screen
CALL CURSOR_BEGIN_R
;----- find next string
;
MOV AX, C_TBL_TAIL
CMP AX, C_TBL_HEAD
JE PNL_EMPTY ; no string found
MOV BX, CURRENT
MOV SI, CS:[BX] ; get current string's end
CMP BX, AX ; current=tail ?
JE NL_L2 ; yes, wraparound
CALL SCROLLDO_TBL ; else, scroll down
CMP BX, AX ; at tail now ?
JNE NL_L3 ; no, go and update CURRENT
MOV SI, CS:[BX] ; else, new old string's end
NL_L2:
MOV BX, C_TBL_HEAD ; wraparound
NL_L3:
MOV CURRENT, BX ; new current string
MOV CX, CS:[BX] ; new string's end
JMP PNL_L1 ; next check the length
CURSOR_BEGIN_R:
;----- moves the cursor to the start of the buffer
;
OR BH, BH ; is cursor at start ?
JZ CBR_DONE ; if so then return
CALL CURSOR_LEFT_R ; move one to the left
JMP CURSOR_BEGIN_R ; and again until at start
CBR_DONE:
RET
CURSOR_END_R:
;----- moves the cursor to the end of the buffer
;
CMP BL, BH ; is cursor at end ?
JE CER_DONE ; yes: return
CALL CURSOR_RIGHT_R ; move one towards the end
JMP CURSOR_END_R ; until done
CER_DONE:
RET
F1_R:
;----- move cursor one word left
;
CALL CURSOR_LEFT_R ;
OR BH, BH ; move one character left
JZ F1_L2 ; until a non-blank is
CMP BYTE PTR [DI-1], ' ' ; reached
JE F1_R ;
F1_L1:
CALL CURSOR_LEFT_R ; now move left until
OR BH, BH ; the next blank
JZ F1_L2 ;
CMP BYTE PTR [DI-1], ' ' ;
JNE F1_L1
F1_L2: ;
RET
F2_R:
;----- move cursor one word right
;
CALL CURSOR_RIGHT_R ;
CMP BL, BH ; move one character right
JE F2_L2 ; until a blank is reached
CMP BYTE PTR [DI], ' ' ;
JNE F2_R
F2_L1:
CALL CURSOR_RIGHT_R
OR BH, BH ; move on until the
JZ F2_L2 ; next non-blank
CMP BYTE PTR [DI], ' ' ;
JE F2_L1 ;
F2_L2: ;
RET
F3_R:
;----- erase from begin of the string to the current cursor position
;
CLR CH
MOV CL, BH
JCXZ F3_L2 ; CX=0: we are at start of string
F3_L1:
PUSH CX ; save number of characters
CALL BACKSPACE_R ; we are going to remove
POP CX ; recover number of characters
LOOP F3_L1 ;
F3_L2:
RET
F4_R:
;----- erase from the current cursor position to the end
;
CLR CH
MOV CL, BL
SUB CL, BH
JZ F4_L2
CALL CURSOR_END_R
F4_L1:
PUSH CX
CALL BACKSPACE_R
POP CX
LOOP F4_L1
F4_L2:
RET
F5_R:
;----- insert current string in STR_BUF and start editing a fresh one
;
MOV BP, SP ; need access to buffer address
; which is saved on the stack
CALL CURSOR_END_R
MOV AL, '@' ; show what is going to happen
CALL DISPLAY_CHAR ;
MOV AL, CR
CALL DISPLAY_CHAR
MOV AL, LF
CALL DISPLAY_CHAR
MOV SI, [BP+2] ; get source pointer from stack
CLR CH
MOV CL, BL
CALL STR_BUF_INSERT
ADD SP, 2 ; discard return address on stack
JMP FI_RESTART
F6_R:
;----- End Of File character on function-key 6
;
MOV AL, EOF
JMP M_L1 ; go insert this character
F7_R:
;----- Nul on function-key 7
;
CLR AL
JMP M_L1 ; go insert this character
CTRL_LEFT_R:
;----- move cursor downwards
;
MOV AX, CURSOR_TYPE
CMP AL, CURSOR_MASK
JE CTRL_LR_X
INC AL
INC AH
MOV CURSOR_TYPE, AX
CALL SET_CURSOR_TYPE
CTRL_LR_X:
RET
CTRL_RIGHT_R:
;----- move cursor upwards
;
MOV AX, CURSOR_TYPE
OR AH, AH
JZ CTRL_RR_X
DEC AL
DEC AH
MOV CURSOR_TYPE, AX
CALL SET_CURSOR_TYPE
CTRL_RR_X:
RET
CTRL_HOME_R:
;----- move cursor-top downwards
;
MOV AX, CURSOR_TYPE
CMP AH, CURSOR_MASK
JE CTRL_HR_X
INC AH
MOV CURSOR_TYPE, AX
CALL SET_CURSOR_TYPE
CTRL_HR_X:
RET
CTRL_END_R:
;----- move cursor-bottom downwards
;
MOV AX, CURSOR_TYPE
CMP AL, CURSOR_MASK
JE CTRL_ER_X
INC AL
MOV CURSOR_TYPE, AX
CALL SET_CURSOR_TYPE
CTRL_ER_X:
RET
CTRL_PGUP_R:
;----- move cursor-top upwards
;
MOV AX, CURSOR_TYPE
OR AH, AH
JZ CTRL_PU_X
DEC AH
MOV CURSOR_TYPE, AX
CALL SET_CURSOR_TYPE
CTRL_PU_X:
RET
CTRL_PGDN_R:
;----- move cursor-bottom upwards
;
MOV AX, CURSOR_TYPE
OR AL, AL
JZ CTRL_PD_X
DEC AL
MOV CURSOR_TYPE, AX
CALL SET_CURSOR_TYPE
CTRL_PD_X:
RET
SHIFT_F5_R:
;----- decrement change in INSERT cursor-shape
;
CMP INSERT_SHIFT, 0
JE SHIFT_F5_X
DEC INSERT_SHIFT
CALL SET_CURSOR_TYPE
SHIFT_F5_X:
RET
SHIFT_F6_R:
;----- increment change in INSERT cursor-shape
;
CMP INSERT_SHIFT, CURSOR_MASK
JE SHIFT_F6_X
INC INSERT_SHIFT
CALL SET_CURSOR_TYPE
SHIFT_F6_X:
RET
ALT_DIGIT:
;----- scroll up through previous strings a number of
; times given by the digit key which was pressed
; along with the ALT-key.
;
SUB AL, ALT_1 ; make use of contiguous range of codes
; generated by the ALT-digit keys
; AL now in range 0 .. 9
CLR CH
MOV CL, AL
JCXZ DA_LX ; so ALT_1 same as arrow-up
PUSH BX
MOV BX, CURRENT ; get CURRENT
DA_L1:
CMP BX, C_TBL_HEAD ; at oldest string in table ?
JNE DA_L2 ; if so, stop, no wrap around
CALL SCROLLDO_TBL ; and undo last scrollup
JMPS DA_L3
DA_L2:
CALL SCROLLUP_TBL ; else one entry up
LOOP DA_L1 ; repeat CX times
DA_L3:
MOV CURRENT, BX ; update CURRENT
POP BX
DA_LX:
JMP PREV_LINE_R ;
MAIN_LOOP_RETURN:
;----- no match in extended ascii found, return to main loop
;
RET
MAIN_LOOP ENDP
REQ_HDR STRUC
LENGTH DB ?
UNIT DB ?
COMMAND DB ?
STATUS DW ?
RESERV DB 8 DUP(?)
REQ_HDR ENDS
INIT_HDR STRUC
DB SIZE REQ_HDR DUP(?)
NO_UNITS DB ?
END_ADR DD ?
PARMS DD ?
INIT_HDR ENDS
IO_HDR STRUC
DB SIZE REQ_HDR DUP(?)
MEDIA DB ?
BUFFER_ADR DD ?
COUNT DW ?
IO_HDR ENDS
;-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
;
; D E V I C E S T R A T E G Y ( ENTRY POINT )
;
;-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
D_STRATEGY PROC FAR
;----- save request header address
;
MOV REQ_HEADER_OFS, BX
MOV REQ_HEADER_SEG, ES
RET
D_STRATEGY ENDP
D_F_TABLE LABEL WORD ; this table contains routine addresses
; for the device-driver functions
DW D_INIT ; 0: driver initialization
DW D_NOP ; 1: media check
DW D_NOP ; 2: build BPB
DW D_IOCTL_IN ; 3: IO-control input
DW D_INPUT ; 4: input
DW D_NON_DSTR_IN ; 5: non-destructive input
DW D_INPUT_STATUS ; 6: input status
DW D_INPUT_FLUSH ; 7: input flush
DW D_OUTPUT ; 8: output
DW D_OUTPUT_VER ; 9: output with verify
DW D_OUTPUT_STATUS ; 10: output status
DW D_OUTPUT_FLUSH ; 11: output flush
DW D_IOCTL_OUT ; 12: IO-control output
DW D_OPEN ; 13: device open
DW D_CLOSE ; 14: device close
DW D_NOP ; 15: removable media
D_F_TABLE_LEN = $ - D_F_TABLE
;-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
;
; D E V I C E I N T E R R U P T ( ENTRY POINT )
;
;-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
D_INTERRUPT PROC FAR ;
STI
PUSH ES ;
PUSH DS ;
PUSH BP ; save
PUSH DI ; registers
PUSH SI ;
PUSH DX ;
PUSH CX ;
PUSH BX ;
PUSH AX
CALL SETSTACK ; to internal stack
MOV BIOS_TTY_SW, ON
MOV ES, REQ_HEADER_SEG ; get request-
MOV BX, REQ_HEADER_OFS ; header block
MOV AL, ES: [BX].COMMAND ; get opcode
CLR AH
SHL AX, 1
CMP AX, D_F_TABLE_LEN
JAE D_ERROR
MOV SI, AX
JMP D_F_TABLE[SI] ; do request
;----- it is assumed that all routines preserve
; the ES:BX pointer to the Request Header
;
D_ERROR:
OR ES: [BX].STATUS, 8003H ; invalid code
D_EXIT:
OR ES: [BX].STATUS, 0100H ; status_ok
MOV BIOS_TTY_SW, OFF
CALL RESETSTACK ; back to
; callers stack
POP AX
POP BX
POP CX ;
POP DX ;
POP SI ; restore
POP DI ; registers
POP BP ;
POP DS ;
POP ES ;
RET
D_INTERRUPT ENDP
D_NOP :
D_IOCTL_IN :
D_INPUT :
D_NON_DSTR_IN :
D_INPUT_STATUS :
D_INPUT_FLUSH :
D_OUTPUT_STATUS :
D_OUTPUT_FLUSH :
D_IOCTL_OUT :
D_OPEN :
D_CLOSE :
JMP D_ERROR
INSTALL_BUF PROC NEAR
;***************************************************************
;* Redirect the DOS-keyboard-buffer pointers to enlarged buffer
;* Copy old buffer contents to the new buffer
;***************************************************************
PUSH DS
CMP KB_INST, ON ;
JNE IB_L1 ; buffer is already here
LEA SI, MSG_KB_PR ; already present message
JMP IB_LX
IB_L1:
CMP KBBUFFER_SIZE, 32 ; worth the trouble ?
JA IB_L2 ;
LEA SI, MSG_KB_NO_1 ; message: no succes
CALL PRINT
MOV AX, KBBUFFER_SIZE
SHR AX, 1
CALL PRINT_NUM
LEA SI, MSG_KB_NO_2
JMP IB_LX
IB_L2:
ASSUME DS: DOS_DATA_AREA ;
MOV AX, DOS_DATA_AREA ; data segment on
MOV DS, AX ; DOS-OS Data
CLR DI
MOV BX, KBBUFFER
CLI
MOV SI, KB_HEAD ; move any data already
IB_L3:
CMP SI, KB_TAIL ; in the DOS-buffer to
JE IB_L4 ; new buffer
CMP DI, 32 ; can not copy indefinitely
JE IB_L4 ; because new buffer overlays
; initialization code
MOV AX, [SI] ;
MOV CS: [BX+DI], AX ; transfer item
INC DI ; keep character count
INC DI ;
INC SI ;
INC SI ;
CMP SI, DOS_BUFFER_END ; wraparound if
JNE IB_L3 ; neccesary
MOV SI, DOS_BUFFER_START ;
JMP IB_L3 ;
IB_L4:
MOV AX, DOS_BUFFER_START
MOV OLD_DOS_BUFFER_START, AX
MOV AX, DOS_BUFFER_END
MOV OLD_DOS_BUFFER_END, AX
MOV AX, CS ;
SUB AX, DOS_DATA_AREA ; make AX point to offset
MOV CL, 4 ; KBBUFFER relative to
SHL AX, CL ; segment DOS_DATA_AREA
ADD AX, KBBUFFER ;
MOV DOS_BUFFER_START, AX ;
MOV KB_HEAD, AX ; change pointers to
MOV KB_TAIL, AX ; our own buffer
ADD KB_TAIL, DI ;
ADD AX, KBBUFFER_SIZE ; add number of characters
MOV DOS_BUFFER_END, AX ; taken over from old buffer
STI
ASSUME DS: NOTHING
MOV KB_INST, ON ; presence-flag on
LEA SI, MSG_KB_IN ; message: succes
CALL PRINT
LEA SI, MSG_KB_1 ; give size of buffer
CALL PRINT
MOV AX, KBBUFFER_SIZE
SHR AX, 1
CALL PRINT_NUM
LEA SI, MSG_KB_2
IB_LX:
CALL PRINT ;
POP DS
RET
INSTALL_BUF ENDP
UNINSTALL_BUF PROC NEAR
;************************************************
;* Restore original keyboard buffer pointers
;*************************************************
PUSH DS
CMP KB_INST, OFF
JE UB_L1
ASSUME DS: DOS_DATA_AREA ;
MOV AX, DOS_DATA_AREA ; data segment on
MOV DS, AX ; DOS-OS Data
CLI
MOV AX, OLD_DOS_BUFFER_START
MOV DOS_BUFFER_START, AX ; restore old
MOV KB_HEAD, AX ; buffer addresses
MOV KB_TAIL, AX ; note:
MOV AX, OLD_DOS_BUFFER_END ; buffer is flushed
MOV DOS_BUFFER_END, AX
STI
ASSUME DS: NOTHING
MOV KB_INST, OFF
LEA SI, MSG_KB_UN ; buffer-gone message
JMPS UB_LX
UB_L1:
LEA SI, MSG_KB_NOT_PR ; error message
UB_LX:
CALL PRINT ; display message
POP DS
RET
UNINSTALL_BUF ENDP
INSTALL_ED PROC NEAR
;**********************************************************
;* Redirect interrupt 21H function 0AH to enable enhanced
;* editing and buffering
;**********************************************************
PUSH ES
CMP EDT_INST, ON ;
JE IE_L2 ; editor already installed
CMP EDT_INST, NOT_INST ; test for first turn on
MOV EDT_INST, ON ; presence-flag on
JNE IE_L1 ; install INT21H interrupt
; address, if not already done
CLR AX ; segment 0 for interrupt-
MOV ES, AX ; address manipulation
LES BX, ES: [DOS_FUNCTION_INT]
; save old address
MOV WORD PTR OLD_FUNCTION_INT, BX
MOV WORD PTR OLD_FUNCTION_INT[2], ES
MOV ES, AX
; replace with new address
CLI
MOV ES: [DOS_FUNCTION_INT], OFFSET FUNCTION_INT_ENTRY
MOV ES: [DOS_FUNCTION_INT + 2], CS
STI
IE_L1:
LEA SI, MSG_EDT_IN ; reinitialize message
CALL PRINT
LEA SI, MSG_EDT_1 ; editor specification
CALL PRINT
MOV AX, STR_BUF_SIZE
CALL PRINT_NUM
LEA SI, MSG_EDT_2
CALL PRINT
MOV AX, C_TBL_SIZE
SHR AX, 1 ; size in bytes --> no-of-entries
DEC AX ; table-size = entries - 1
CALL PRINT_NUM
LEA SI, MSG_EDT_3
JMPS IE_LX
IE_L2:
LEA SI, MSG_EDT_PR ; already present message
IE_LX:
CALL PRINT ;
POP ES
RET
INSTALL_ED ENDP
UNINSTALL_ED PROC NEAR
;*****************************************************
;* restore INT 21H interrupt address
;* if not possible, zero flag is OFF at exit
;*****************************************************
PUSH ES
CMP EDT_INST, ON
JNE UE_L2
MOV EDT_INST, OFF
CLR AX
MOV ES, AX
;----- check whether our address still in vector
;
CMP ES: [DOS_FUNCTION_INT], OFFSET FUNCTION_INT_ENTRY
JNE UE_L1
MOV AX, CS
CMP ES: [DOS_FUNCTION_INT + 2], AX
JNE UE_L1
;----- OK, undo revectoring
;
MOV AX, WORD PTR OLD_FUNCTION_INT
MOV ES: [DOS_FUNCTION_INT], AX
MOV AX, WORD PTR OLD_FUNCTION_INT[2]
MOV ES: [DOS_FUNCTION_INT + 2], AX
MOV EDT_INST, NOT_INST
UE_L1:
LEA SI, MSG_EDT_UN ; editor-gone message
JMPS UE_LX
UE_L2:
LEA SI, MSG_EDT_NOT_PR
UE_LX:
CALL PRINT ; editor-already-gone message
POP ES
RET
UNINSTALL_ED ENDP
RESET_EDT PROC NEAR
MOV AX, C_TBL_START
MOV C_TBL_HEAD, AX
MOV C_TBL_TAIL, AX
MOV CURRENT, AX
RET
RESET_EDT ENDP
INSTALL_ANSI PROC NEAR
MOV ANSI, ON
MOV MAX_COL, 80
RET
INSTALL_ANSI ENDP
UNINSTALL_ANSI PROC NEAR
MOV ANSI, OFF
RET
UNINSTALL_ANSI ENDP
RECALL PROC NEAR
;----- display all strings in STR_BUF
;
CLR DX ; initialize
MOV DI, C_TBL_SIZE ; DX = sequence number
SHR DI, 1 ; DI = maximum number of strings
DEC DI ;
MOV BX, C_TBL_TAIL ; BX = pointer into C_TBL entry
MOV SI, CS:[BX] ; of string currently displayed
MOV BX, C_TBL_HEAD ;
REC_L1:
CMP BX, C_TBL_TAIL ;
JE REC_LX ; all strings done, normal exit
MOV AL, CR
CALL WRITE_CHAR
MOV AL, LF
CALL WRITE_CHAR
INC DX
CMP DX, DI ; savety check, prevent endless looping
JA RESET_EDT ; this jump should not occur, error exit
MOV AX, DX
CALL PRINT_NUM
MOV AL, '.'
CALL WRITE_CHAR
MOV AL, ' '
CALL WRITE_CHAR
MOV CX, CS:[BX] ; last character of string
SUB CX, SI ; compute length
JAE REC_L2
ADD CX, STR_BUF_SIZE
REC_L2:
INC SI ; next character
CMP SI, STR_BUF_END
JNE REC_L3 ; wrap around if necessary
MOV SI, STR_BUF_START
REC_L3:
MOV AL, CS:[SI]
CALL DISPLAY_COMPLEX
LOOP REC_L2
CALL SCROLLDO_TBL
JMP REC_L1
REC_LX:
RET
RECALL ENDP
D_OUTPUT:
D_OUTPUT_VER:
;************************************************************
;* Output routine.
;* Characters output to this device are interpreted as
;* control codes to switch the editor and buffer on and off.
;* Such control codes consist always of two characters:
;* "IK" = Install Keyboard Buffer.
;* "IE" = Install Keyboard Editor.
;* "UK" = Uninstall Keyboard Buffer.
;* "UE" = Uninstall Keyboard Editor.
;* "RE" = Reset Editor
;* "IA" = ANSI mode On
;* "UA" = ANSI mode Off
;*
;* On entry: ES:BX contains long pointer to Request Header
;************************************************************
PUSH ES
PUSH BX
MOV CX, ES: [BX].COUNT ; number of characters in CX
OR CX, CX ;
JZ O_LX ; check for a mistake
LES BX, ES: [BX].BUFFER_ADR ; load address of buffer
MOV AL, ES: [BX] ; get first character from buffer
CMP EXPECTSECOND, ON; second part expected?
JE O_L1 ; yes: treat second part
MOV FIRSTPART, AL ; no: save this first part
MOV EXPECTSECOND, ON; now we do expect the second part
CMP CX, 1 ; was there more then one?
JBE O_LX ; no: wait for next arrival
MOV AL, ES: [BX+1] ; yes: get second character
;----- at this point there is a complete command
; and we are ready for interpreting
;
O_L1:
MOV EXPECTSECOND, OFF
MOV AH, FIRSTPART ; AX now contains the
; two letter code
AND AX, 0DFDFH ; capitalize
CMP AX, 'IK' ;
JNE O_L2
CALL INSTALL_BUF ; code = 'IK'
JMPS O_LX
O_L2:
CMP AX, 'IE' ;
JNE O_L3
CALL INSTALL_ED ; code = 'IE'
JMPS O_LX
O_L3:
CMP AX, 'UK' ;
JNE O_L4
CALL UNINSTALL_BUF ; code = 'UK'
JMPS O_LX
O_L4:
CMP AX, 'UE' ;
JNE O_L6
CALL UNINSTALL_ED ; code = 'UE'
JMPS O_LX
O_L6:
CMP AX, 'IA' ;
JNE O_L7
CALL INSTALL_ANSI ; code = 'IA'
JMPS O_LX
O_L7:
CMP AX, 'UA' ;
JNE O_L8
CALL UNINSTALL_ANSI ; code = 'UA'
JMPS O_LX
O_L8:
CMP AX, 'RC' ;
JNE O_L9
CALL RECALL ; code = 'UA'
JMPS O_LX
O_L9:
CMP AX, 'RE'
JNE O_LX
CALL RESET_EDT ; code = 'RE'
;----- otherwise invalid combination
;
O_LX:
POP BX
POP ES
JMP D_EXIT ; to exit protocol
;----- round origin to paragraph boundary
;
ORG ($ - START_CODE + 15) AND 0FFF0H
END_CODE = $
ACTIVATE DB OFF ; activate immediate switch
P_CHAR_COUNT DW ? ; character count on parameter line
MSG_INVA_SW_1 DB ' Invalid switch at position : ', NULL
MSG_INVA_SW_2 DB ' in Configuration file'
DB CR, LF, LF, NULL
MSG_INVA_CHR_1 DB ' Invalid character at position : ', NULL
MSG_INVA_CHR_2 DB ' in Configuration file'
DB CR, LF, LF, NULL
MSG_ADR_OVL DB ' Buffer sizes too large, reduce parameter values'
DB ' in Configuration file'
DB CR, LF
DB ' Default values substituted'
DB CR, LF, LF, NULL
MSG_OVL_1 DB ' Parameter value overflow at position: ', NULL
MSG_OVL_2 DB ' in Configuration file'
DB CR, LF, NULL
MSG_ADJUST_1 DB ' Specified size for keyboard buffer too large,'
DB ' size adjusted to : ', NULL
MSG_ADJUST_2 DB ' bytes', CR, LF, NULL
GET_PARMS PROC NEAR
;*************************************************************
; This procedure scans the string addressed by DS:SI for
; the presence of parameters to the driver.
;
; Parameters must have the following format:
;
; DEVICE=\path\drv_name K:nnn B:nnn C:nnn [/A] [/N]
;
; where nnn is a decimal number.
;
; K:nnn nnn is keyboard buffer size
; B:nnn nnn is buffer size for command strings
; C:nnn nnn is maximum number of commands that can be stored
; /A if present: activation on driver installation
; /N if present: cursor control through ANSI-codes
;
;
; The code should implement the following:
;
; ----------------------
;
; C: CHAR;
; Separators = [NULL, ' ', ',', '=', ';', '+', TAB];
;
; ON EndOfLine: EXIT;
; REPEAT GetChar(C) UNTIL C IN Separators;
; REPEAT
; WHILE (C IN Separators) DO GetChar(C);
; CASE C OF
; 'K': OUT1 := GetNum;
; 'B': OUT2 := GetNum;
; 'C': OUT3 := GetNum;
; '/': [GetChar(C); IF C='A' THEN ACTIVATE := ON
; ELSE IF C='N' THEN ANSI := ON
; ];
;
; UNTIL EndOfLine;
;
; GetNum:
; GetNum := 0;
; IF C = ':' THEN GetChar(C);
; WHILE (C IN ['0'..'9']) DO [
; GetNum := GetNum*10 + (Ord(C) - Ord('0'));
; GetChar(C)
; ];
;
; ---------------------
;
;
LDS SI, ES: [BX].PARMS
MOV P_CHAR_COUNT, 7 ; assume 'DEVICE=' occupies
; first 7 positions in CONFIG
GP_L1:
CALL GET_P_CHAR
JZ GP_LX
CALL IN_SEPARATORS
JNZ GP_L1
GP_NEXT:
CALL GET_P_CHAR
JZ GP_LX
CALL IN_SEPARATORS
JZ GP_NEXT
CMP AL, 'K'
JNE GP_LB
;----- collect keyboard buffer size
;
CALL GET_NUM
OR CX, CX ; check result
JZ GP_LK1 ; zero ?
JS GP_LO ; > 32K ?
SHL CX, 1 ; each keystroke fills two bytes
MOV KBBUFFER_SIZE, CX ; in buffer
GP_LK1:
JMP GP_NEXT
GP_LB:
CMP AL, 'B'
JNE GP_LC
;----- collect string buffer size
;
CALL GET_NUM
OR CX, CX ; check result
JZ GP_LB1
MOV STR_BUF_SIZE, CX
GP_LB1:
JMP GP_NEXT
GP_LC:
CMP AL, 'C'
JNE GP_LS
;----- collect maximum number of commands
;
CALL GET_NUM
OR CX, CX ; check result
JZ GP_LC1
INC CX ; table-size = no-of-entries + 1
JS GP_LO ; > 32K ?
SHL CX, 1 ; CX = no-of-entries, convert to bytes
MOV C_TBL_SIZE, CX
GP_LC1:
JMP GP_NEXT
GP_LX:
RET
GP_LS:
CMP AL, '/'
JNE GP_L6
;----- collect switches
;
CALL GET_P_CHAR
JZ GP_LX
CMP AL, 'A'
JNE GP_LS1
MOV ACTIVATE, ON
JMP GP_NEXT
GP_LS1:
CMP AL, 'N'
JNE GP_LS2
MOV ANSI, ON
JMP GP_NEXT
GP_LS2:
;----- invalid switch
;
PUSH SI
LEA SI, MSG_INVA_SW_1
CALL PRINT
MOV AX, P_CHAR_COUNT
CALL PRINT_NUM
LEA SI, MSG_INVA_SW_2
CALL PRINT
POP SI
JMP GP_L1
GP_LO:
;----- overflow
;
PUSH SI
LEA SI, MSG_OVL_1
CALL PRINT
MOV AX, P_CHAR_COUNT
DEC AX
CALL PRINT_NUM
LEA SI, MSG_OVL_2
CALL PRINT
POP SI
JMP GP_NEXT
GP_L6:
;----- invalid character on line
;
PUSH SI
LEA SI, MSG_INVA_CHR_1
CALL PRINT
MOV AX, P_CHAR_COUNT
CALL PRINT_NUM
LEA SI, MSG_INVA_CHR_2
CALL PRINT
POP SI
JMP GP_L1 ; go on after next separator
IN_SEPARATORS PROC NEAR
;----- determine if character is in the separator set
; in: AL: character
; return: zero flag set if Al in separators
; !! CR and LF are also return zero
;
CMP AL, NULL
JE IS_LX
CMP AL, ' '
JE IS_LX
CMP AL, ','
JE IS_LX
CMP AL, '='
JE IS_LX
CMP AL, '+'
JE IS_LX
CMP AL, ';'
JE IS_LX
CMP AL, TAB
JE IS_LX
CMP AL, CR
JE IS_LX
CMP AL, LF
IS_LX:
RET
IN_SEPARATORS ENDP
GET_P_CHAR PROC NEAR
;----- return next character from parameter-string in AL
; if EndOfLine reached then zero-flag is set
;
CMP AL, CR
JE GPC_LX
LODSB
INC P_CHAR_COUNT
CMP AL, CR
JE GPC_LX
CMP AL, LF
GPC_LX:
RET
GET_P_CHAR ENDP
PUT_P_CHAR PROC NEAR
;----- reverse last action of GET_P_CHAR
; AL is pushed back onto the "input-stream"
;
DEC SI
DEC P_CHAR_COUNT
RET
PUT_P_CHAR ENDP
GET_NUM PROC NEAR
;----- read a decimal number from parameter string
; out: CX contains the number
; this routine reads only digits, when an other
; character is read it is pushed back through
; PUT_P_CHAR and the routine exits
;
CLR CX
CALL GET_P_CHAR
JZ GN_LX
CMP AL, ':'
JNE GN_L2
GN_L1:
CALL GET_P_CHAR
JZ GN_LX
GN_L2:
CMP AL, '9' ; test for digit
JA GN_LX
CMP AL, '0'
JB GN_LX
SUB AL, '0'
XCHG CX, AX ; multiplication operand in AX
MUL TEN ; x 10
XCHG CX, AX
JNC GN_L3 ; test for overflow
CLR CX ; if so, return zero
JMPS GN_LX1
GN_L3:
CLR AH ;
ADD CX, AX ; add next digit
JMP GN_L1
GN_LX:
CALL PUT_P_CHAR
GN_LX1:
RET
GET_NUM ENDP
GET_PARMS ENDP
D_INIT PROC NEAR
;************************************************************
;* Driver initialization routine.
;*
;* !! version 2.0 only installs driver at boot-time
;* !! NO immediate activation of buffers and editor
;************************************************************
PUSH ES
PUSH BX
MOV BIOS_TTY_SW, ON
MOV AX, CS
MOV DS, AX ; tell about the instal-
LEA SI, MSG_VERSION ; lation of this driver
CALL PRINT ;
CALL GET_PARMS
;----- check the parameters
;
MOV CL, 4
MOV AX, KBBUFFER_SIZE ; compute end of KBBUFFER
ADD AX, 0FH ;
SHR AX, CL ; paragraph format
ADD AX, (END_CODE - START_CODE) SHR 4
MOV CX, CS ; add segment paragraph number
ADD AX, CX ;
SUB AX, 1000H ; must not exceed first 64K
JBE D_IN_L2
MOV CL, 4 ;
SHL AX, CL ; in bytes again
SUB KBBUFFER_SIZE, AX ; adjust KBBUFFER_SIZE
JNC D_IN_L1 ; no negative values
MOV KBBUFFER_SIZE, 0
D_IN_L1:
LEA SI, MSG_ADJUST_1 ;
CALL PRINT ; display message
MOV AX, KBBUFFER_SIZE ; and new size of KBBUFFER
CALL PRINT_NUM ;
LEA SI, MSG_ADJUST_2 ;
CALL PRINT
D_IN_L2:
LEA AX, END_CODE
MOV KBBUFFER, AX ; set start address of KB-buffer
ADD AX, KBBUFFER_SIZE
MOV STR_BUF_START, AX ; initialize command string
MOV STR_BUF_HEAD, AX ; buffer data-structure
MOV STR_BUF_TAIL, AX ;
ADD AX, STR_BUF_SIZE ;
JC D_IN_ERR ; test for overflow
MOV STR_BUF_END, AX ;
MOV SI, AX ; initialize first entry
MOV CS:[SI], AX ; in C_TBL with address of
DEC WORD PTR CS:[SI] ; last byte in STR_BUF
MOV C_TBL_START, AX ; start address of C_TBL
MOV C_TBL_HEAD, AX ;
MOV C_TBL_TAIL, AX ;
MOV CURRENT, AX
ADD AX, C_TBL_SIZE ; now AX = end of resident code
JC D_IN_ERR ; test for overflow
MOV C_TBL_END, AX ; end of C_TBL
;----- fill in in Request Header
;
MOV WORD PTR ES: [BX].END_ADR, AX
MOV WORD PTR ES: [BX].END_ADR+2, CS
CMP ACTIVATE, ON ; /A switch present ?
JNE D_IN_LX
CALL INSTALL_BUF ; if so, install now
CALL INSTALL_ED ;
D_IN_LX:
MOV BIOS_TTY_SW, OFF ; display through DOS
POP BX
POP ES
JMP D_EXIT ; initialization done:
; to exit protocol
D_IN_ERR:
LEA SI, MSG_ADR_OVL ; address-overflow message
CALL PRINT ;
MOV C_TBL_SIZE, 32 ; set default values
MOV STR_BUF_SIZE, 512 ; these values should not
MOV ACTIVATE, OFF ; cause trouble
JMP D_IN_L2 ; try again
D_INIT ENDP
CODE ENDS
END